﻿package engine.game.world {
	import engine.game.creature.Creature;
	import flash.geom.Rectangle;
	import flash.net.URLLoader;
	import flash.events.EventDispatcher;
	import flash.display.BitmapData;
	import flash.display.Bitmap;
	import flash.events.Event;
	import flash.geom.Point;
	import engine.core.Array2;
	import engine.managers.AssetManager;
	import flash.net.URLRequest;
	import engine.base.GameBase;
	import engine.game.Camera;

	/**
	* This class represents an area map in the game.
	* @author Jon Smelquist
	*/
	public class Map extends GameBase
	{
		private var _width:Number;
		private var _height:Number; 
		private var _playerSpawn:Point;
		private var _playerSpawnDir:int;
		private var _mapName:String;
		private var _loader:URLLoader;
		private var _baseTiles:MapLayer;
		private var _overlayTiles:MapLayer;
		private var _creatures:Array;
		
		public function Map(name:String)
		{
			super(100);
			_mapName = name;
			_creatures = new Array();
		}
		
		public function getTile(pixelPosition:Point):Tile {
			return _baseTiles.getTile(int(pixelPosition.y/32),int(pixelPosition.x/32));
		}
		
		public function get playerSpawn():Point
		{
				return _playerSpawn;
		}
		
		public function get playerSpawnDir():int
		{
				return _playerSpawnDir;
		}
		
		public function get width():int {
			return _width;
		}
		
		public function get height():int {
			return _height;
		}
		
		public function get base():MapLayer
		{
			return _baseTiles;
		}
		
		public function get overlay():MapLayer
		{
			return _overlayTiles;
		}
		
		public function addCreature(creature:GameBase):void
		{
			_creatures.push(creature);
		}
		
		public function get creatures():Array
		{
			return _creatures;
		}
		
		public function get mapName():String
		{
			return _mapName;
		}
		
		/**
		 * Process tick for map by looping through all visible tiles and call
		 * their processTick methods.
		 */
		protected override function update():void 
		{
			_baseTiles.processTick(_msTick);
			for each (var c:Creature in _creatures)
			{
				c.processTick(_msTick);
			}
			_overlayTiles.processTick(_msTick);
		}
		
		/**
		 * Draw the map by looping through all visible tiles and draw
		 * them at their repective locations
		 */
		public override function draw(surface:BitmapData, camRect:Rectangle):void
		{
			_baseTiles.draw(surface, camRect);
			_overlayTiles.draw(surface, camRect);
		}
		
		/**
		 * Load the map from the source XML file.
		 * @param	customSpawn		Optional Paramater to allow custom location to place the player
		 * @param	customDirection	Optional Paramater to allow custom direction to place the player
		 */
		public function loadMap(customSpawn:Point = null, customDirection:int = 1):void {
			// Utilize the information on where to place the player
			if (customSpawn != null){
				_playerSpawn = customSpawn;
			}
			_playerSpawnDir = customDirection;
			
			_loader = new URLLoader();
            _loader.addEventListener( Event.COMPLETE, mapLoaded );
            var request:URLRequest = new URLRequest( "maps/" + _mapName + ".xml" );
			try {
				_loader.load( request );
			}
			catch (error:Error) {
                throw new Error("The map: "+_mapName+", could not be found.");
            }

		}
		
		private function mapLoaded(e:Event):void {
			
			trace("Map loaded, processing data...");
			
			var mapXML:XML = new XML( _loader.data );
			_width = int( mapXML.@w );
			_height = int( mapXML.@h );
			
			var asset:String = mapXML.@chipset;
			_baseTiles = new MapLayer(_width, _height, asset);
			_overlayTiles = new MapLayer(_width, _height, asset);
			
			// Load chipset for map based on map data
			_source = AssetManager.getInstance().getAsset(asset);
			
			// If we weren't sent a custom spawn point, use the maps defined spawn
			if (_playerSpawn == null){
				_playerSpawn = new Point(int(mapXML.@px) * 32, int(mapXML.@py) * 32);
			}
			
			// Used for loops
			var tilenum:Number;
			var y:int;
			var x:int;
			var tileLayerData:Array;
			var i:int;
			var tempData:Array;
			var curTile:Tile;
			
			// split the rows, removing <layer> from beginning
			tempData = mapXML.floor.toXMLString().split( "|" );
			trace(tempData);
			// load each row into a 2d array of tilenums
			var dataLayer:Array = new Array();
			for ( i = 0; i < tempData.length; i++ ) {
				dataLayer.push( tempData[ i ].split( "," ) );
			}
				
			for (y = 0; y < _height; y++) {
				for (x = 0; x < _width; x++) {	
					tilenum = dataLayer[y][x];	
					_baseTiles.setTile(y, x, new Tile(tilenum, new Point(x*32,y*32), false));		
				}
			}
			
			// split the rows, removing <layer> from beginning
			tempData = mapXML.terrain.toXMLString().split( "|" );
			// load each row into a 2d array of tilenums
			dataLayer = new Array();
			for ( i = 0; i < tempData.length; i++ ) {
				dataLayer.push( tempData[ i ].split( "," ) );
			}
				
			for (y = 0; y < _height; y++) {
				for (x = 0; x < _width; x++) {	
					tilenum = dataLayer[y][x];
					if ( tilenum != -1 ) {	
						curTile = _baseTiles.getTile(y, x);
						curTile.addImageLayer(tilenum);	
					}
				}
			}
			
			tempData = mapXML.collision.toXMLString().split( "|" );
			// load each row into a 2d array of tilenums
			dataLayer = new Array();
			for ( i = 0; i < tempData.length; i++ ) {
				dataLayer.push( tempData[ i ].split( "," ) );
			}
				
			for (y = 0; y < _height; y++) {
				for (x = 0; x < _width; x++) {		
					tilenum = dataLayer[y][x];
					if ( tilenum != -1 ) {
						curTile = _baseTiles.getTile(y, x);
						curTile.blocks = true;
					}
				}
			}
			
			// split the rows, removing <layer> from beginning
			tempData = mapXML.roof.toXMLString().split( "|" );
			// load each row into a 2d array of tilenums
			dataLayer = new Array();
			for ( i = 0; i < tempData.length; i++ ) {
				dataLayer.push( tempData[ i ].split( "," ) );
			}
				
			for (y = 0; y < _height; y++) {
				for (x = 0; x < _width; x++) {		
					tilenum = dataLayer[y][x];	
					_overlayTiles.setTile(y, x, new Tile(tilenum, new Point(x*32,y*32), false));		
				}
			}
			
			// split the rows, removing <layer> from beginning
			tempData = mapXML.roof_add.toXMLString().split( "|" );
			// load each row into a 2d array of tilenums
			dataLayer = new Array();
			for ( i = 0; i < tempData.length; i++ ) {
				dataLayer.push( tempData[ i ].split( "," ) );
			}
				
			for (y = 0; y < _height; y++) {
				for (x = 0; x < _width; x++) {		
					tilenum = dataLayer[y][x];
					if ( tilenum != -1 ) {
						curTile = _overlayTiles.getTile(y, x);
						curTile.addImageLayer(tilenum);	
					}
				}
			}
			
			
			
			trace("Map has been processed, starting game timers...");
			dispatchEvent( new Event(Event.COMPLETE) );
		}
		
		public static function loadMap(map:Map, completeHandler:Function, customSpawn:Point = null, customDirection:int = 1):void {
			map.addEventListener( Event.COMPLETE, completeHandler, false, 0, true );
				
			Camera.getInstance().resetCameraPosition();
				
			if (customSpawn != null) {
				map.loadMap(customSpawn,customDirection);
			}else {
				map.loadMap();
			}
		}
		
	}
	
}